(**************************************************************
   Call Python
 **************************************************************)
unit LishModl;

interface

uses
  Types, SysUtils, Models, Actions;

procedure Initialize;
procedure Finalize;
function NewLishData (Form: IMainForm; path: string): TLishData;
procedure ReleaseLishData (data: TLishData);
procedure CallCmd (Form: IMainForm; cmd: string; filelist: TFileList);
procedure KeyEvent(Form: IMainForm; Key: Integer; MetaKey: TMetaKey;
                   filelist: TFileList);
procedure MouseEvent (Form: IMainForm; EventType: TMouseEventType;
                      MouseKey: TMouseKey; MetaKey: TMetaKey;
                      filelist: TFileList);
procedure ContextMenu(Form: IMainForm; filelist: TFileList);

implementation

uses Python, PyActions;

var
  Initialized: Boolean = False;
  pLish,
  pLish_NewLishData,
  pLish_CallCmd,
  pLish_KeyEvent,
  pLish_MouseEvent,
  pLish_ContextMenu: PPyObject;

function func(fname: string): PPyObject;
  begin
    Result := Dict_GetItemStr(pLish, PChar(fname));
    if not Assigned(Result) then
      raise EPyException.Create('Failed to get ' + fname + ' function.');
    if 0 = IsCallable(Result) then
      raise EPyException.Create(fname + ' is not a function.');
  end;

procedure Initialize;
  var pName, pModl: PPyObject;
  begin
    if not Initialized then
    try
      try
        Python.Initialize;
        Python.InitModule('lish._actions', @METHODS);

        pName := str('lish._control');
        pModl := Python.Import(pName);
        dref(pName);
        if not Assigned(pModl) then
          raise EPyException.Create('Failed to initialize Lish module.');

        pLish := Module_GetDict(pModl);
        pLish_NewLishData := func('new_lish_data');
        pLish_CallCmd := func('call_cmd');
        pLish_KeyEvent := func('key_event');
        pLish_MouseEvent := func('mouse_event');
        pLish_ContextMenu := func('contextmenu');

      except
        on EPyException do begin
          if Assigned(PyErr_Occurred()) then
            PyErr_Print;
          raise;
        end;
      end;
    finally
      Initialized := True;
    end;
  end;

procedure Finalize;
  begin
    if Initialized then begin
      Python.Finalize;
    end;
  end;

function NewLishData (Form: IMainForm; path: string): TLishData;
  begin
    Result := TLishData(call(pLish_NewLishData, [ptr(Pointer(Form)), str(path)]));
  end;

procedure ReleaseLishData (data: TLishData);
  begin
    dref(data);
  end;

procedure CallEvent (pFunc: PPyObject; Form: IMainForm; filelist: TFileList;
                     pArgs: array of PPyObject);
  var
    I: Integer;
    selected: TIntegerDynArray;
    pSelected: array of PPyObject;
    pArgs2: array of PPyObject;
    pRes: PPyObject;
    pResult: PWideChar;
    Result: WideString;
  begin
    selected := filelist.SelectedIndices;
    SetLength(pSelected, Length(selected));
    for I := 0 to Length(selected) - 1 do begin
      pSelected[I] := int(selected[I]);
    end;

    SetLength(pArgs2, 3 + Length(pArgs));
    pArgs2[0] := Form.Lish;
    pArgs2[1] := int(filelist.ItemIndex);
    pArgs2[2] := tuple(pSelected);
    if 0 < Length(pArgs) then
      Move(pArgs[0], pArgs2[3], Length(pArgs) * SizeOf(PPyObject));

    ref(Form.Lish);
    pRes := call(pFunc, pArgs2);
    try
      fromRes(pRes, 'u', [@pResult]);
      Result := WideString(pResult);
      if '' <> Result then
        raise EPyException.Create(Result)
    finally
      dref(pRes);
    end;
  end;

procedure CallCmd (Form: IMainForm; cmd: string; filelist: TFileList);
  begin
    CallEvent(pLish_CallCmd, Form, filelist, [str(cmd)]);
  end;

procedure KeyEvent(Form: IMainForm; Key: Integer; MetaKey: TMetaKey;
                   filelist: TFileList);
  begin
    CallEvent(pLish_KeyEvent, Form, filelist, [int(Key), int(Ord(MetaKey))]);
  end;

procedure MouseEvent (Form: IMainForm; EventType: TMouseEventType;
                      MouseKey: TMouseKey; MetaKey: TMetaKey;
                      filelist: TFileList);
  begin
    CallEvent(pLish_MouseEvent, Form, filelist,
              [int(Ord(EventType)), int(Ord(MouseKey)), int(Ord(MetaKey))]);
  end;

procedure ContextMenu(Form: IMainForm; filelist: TFileList);
  begin
    CallEvent(pLish_ContextMenu, Form, filelist, []);
  end;

end.
